package com.bluefox.modules.quartz.utils;

import com.bluefox.utils.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;

/**
 * @ClassName QuartzRunnable
 * @Description QuartzRunnable
 * @Author SunFeng
 * @Date 2020/2/28 15:25
 * @Version 1.0
 */
@Slf4j
public class QuartzRunnable implements Callable {
    private final Object target;
    private final Method method;
    private final String params;
    private final List<Object[]> methodParams;

    /**
     * 根据beanName methodName, params执行指定任务
     *
     * @param beanName   具体任务实体名称
     * @param methodName 方法名称
     * @param params     参数
     * @throws NoSuchMethodException
     * @throws SecurityException
     */
    QuartzRunnable(String beanName, String methodName, String params)
            throws NoSuchMethodException, SecurityException {
        //从spring容器中获取bean
        this.target = SpringContextHolder.getBean(beanName);
        this.params = params;
        this.methodParams = params.equals("") ? new ArrayList<Object[]>() : getMethodParams(params);

        //目前支持单参数的方法 后期可以扩展
        if (StringUtils.isNotBlank(params)) {
            this.method = target.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
        } else {
            this.method = target.getClass().getDeclaredMethod(methodName);
        }
    }

    @Override
    public Object call() throws Exception {
        //将一个方法设置为可调用，主要针对private方法  这个方法也可以设置字段构造器
        ReflectionUtils.makeAccessible(method);
        if (StringUtils.isNotBlank(params)) {
            method.invoke(target, getMethodParamsValue(methodParams));
        } else {
            method.invoke(target);
        }
        return null;
    }

    /**
     * 获取参数值
     *
     * @param methodParams 参数相关列表
     * @return 参数值列表
     */
    private static Object[] getMethodParamsValue(List<Object[]> methodParams) {
        Object[] classs = new Object[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams) {
            classs[index] = (Object) os[0];
            index++;
        }
        return classs;
    }

    /**
     * 获取参数类型
     *
     * @param methodParams 参数相关列表
     * @return 参数类型列表
     */
    private static Class<?>[] getMethodParamsType(List<Object[]> methodParams) {
        Class<?>[] classs = new Class<?>[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams) {
            classs[index] = (Class<?>) os[1];
            index++;
        }
        return classs;
    }

    /**
     * 获取method方法参数相关列表
     * 目前只支持基本类型
     *
     * @param methodParam 目标字符串
     * @return method方法相关参数列表
     */
    private static List<Object[]> getMethodParams(String methodParam) {
        String[] methodParams = methodParam.split(",");
        List<Object[]> classs = new LinkedList<>();
        for (int i = 0; i < methodParams.length; i++) {
            String str = StringUtils.trimToEmpty(methodParams[i]);
            // String字符串类型，包含'
            if (StringUtils.contains(str, "'")) {
                classs.add(new Object[]{StringUtils.replace(str, "'", ""), String.class});
            }
            // boolean布尔类型，等于true或者false
            else if (StringUtils.equals(str, "true") || StringUtils.equalsIgnoreCase(str, "false")) {
                classs.add(new Object[]{Boolean.valueOf(str), Boolean.class});
            }
            // long长整形，包含L
            else if (StringUtils.containsIgnoreCase(str, "L")) {
                classs.add(new Object[]{Long.valueOf(StringUtils.replaceIgnoreCase(str, "L", "")), Long.class});
            }
            // double浮点类型，包含D
            else if (StringUtils.containsIgnoreCase(str, "D")) {
                classs.add(new Object[]{Double.valueOf(StringUtils.replaceIgnoreCase(str, "D", "")), Double.class});
            }
            // 其他类型归类为整形
            else {
                classs.add(new Object[]{Integer.valueOf(str), Integer.class});
            }
        }
        return classs;
    }

}
